<?php
/**
 * Created by 荣耀电竞.
 * User: 林子彦 <278805354@qq.com>
 * Date: 2018/8/10 0010
 * Time: 11:16
 */
namespace server;
class TaskMgr
{

    const TASK_TAKEN = -1;

    const TASK_FULL = -2;

    const TASK_OK = -3;

    const TASK_NOT_ENOUGH = -4;

    private $_arrValidTasks = null;

    private $taskLock = null;

    private $tasksCfg = null;

    private $swSpareTaskProcesses = null;

    private $swWorkingTaskProcesses = null;

    public function __construct( $http )
    {

        /***
         * 加载配置文件，作用无
         */
        $this->loadTasksCfg();

        /**
         * 验证配置是否正确
         */
        if (!$this->checkCfgValid( $http )) {
            echo '任务进程数设置有误，请重设后再试！';

            var_dump( $this->_arrValidTasks, $http->setting );

            exit(1);
        }

        /**
         * 进程锁
         */
        $this->initTaskLock();

        $this->initSwooleTableForTasks();

    }
    private function loadTasksCfg()
    {
        $this->tasksCfg = include dirname( __DIR__ ) . '/config/tasks.php';

        $arrTasks = [];

        foreach( $this->tasksCfg->tasks as $key => $task )
        {
            if( $task->enable )
            {
                $arrTasks[ $key ] = $task->toArray();
            }
        }

        $this->_arrValidTasks = $this->tasksCfg->tasks->toArray();

    }
    public function checkCfgValid( $http )
    {
        $iTaskWorkerAll = 0;

        foreach( $this->tasksCfg->tasks as $task )
        {
            if( !$task->enable )
            {
                continue;
            }

            if( isset( $task->process_num ) )
            {
                $iTaskWorkerAll += $task->process_num;
            }
            else
            {
                $iTaskWorkerAll += $this->tasksCfg->default->process_num;
            }

        }

        if( $iTaskWorkerAll > $http->setting[ 'task_worker_num' ])
        {
            return false;
        }

        return true;
    }
    private function initTaskLock()
    {
        $this->taskLock = new \swoole_lock(SWOOLE_MUTEX);
    }

    private function initSwooleTableForTasks()
    {
        // 创建内存表
        $this->swSpareTaskProcesses = new \swoole_table( $this->tasksCfg->swSpareTaskSize );
        $this->swSpareTaskProcesses->column( 'task_id', \swoole_table::TYPE_INT, 4 );//WorkStart时分析的id
        $this->swSpareTaskProcesses->column( 'task_index', \swoole_table::TYPE_INT, 4 );//WorkStart时分析的id
        $this->swSpareTaskProcesses->column( 'worker_pid', \swoole_table::TYPE_INT, 4 );//task worker process id
        $this->swSpareTaskProcesses->column( 'taskLabel', \swoole_table::TYPE_STRING, 256 );//task label
        $this->swSpareTaskProcesses->column( 'worker_begin_time_readable', \swoole_table::TYPE_STRING, 32 );//worker process begin time
        $this->swSpareTaskProcesses->column( 'task_begin_time_readable', \swoole_table::TYPE_STRING, 32 );//specified task begin time
        $this->swSpareTaskProcesses->column( 'worker_begin_time', \swoole_table::TYPE_INT, 8 );//worker process begin time
        $this->swSpareTaskProcesses->column( 'task_begin_time', \swoole_table::TYPE_INT, 8 );//specified task begin time
        $this->swSpareTaskProcesses->create();


        //创建内存表
        $this->swWorkingTaskProcesses = new \swoole_table( $this->tasksCfg->swWorkingTasksSize );
        $this->swWorkingTaskProcesses->column( 'task_id', \swoole_table::TYPE_INT, 4 );//WorkStart时分析的id
        $this->swWorkingTaskProcesses->column( 'task_index', \swoole_table::TYPE_INT, 4 );//WorkStart时分析的id
        $this->swWorkingTaskProcesses->column( 'worker_pid', \swoole_table::TYPE_INT, 4 );//task worker process id
        $this->swWorkingTaskProcesses->column( 'taskLabel', \swoole_table::TYPE_STRING, 256 );//task label
        $this->swWorkingTaskProcesses->column( 'worker_begin_time_readable', \swoole_table::TYPE_STRING, 32 );//worker process begin time
        $this->swWorkingTaskProcesses->column( 'task_begin_time_readable', \swoole_table::TYPE_STRING, 32 );//specified task begin time
        $this->swWorkingTaskProcesses->column( 'worker_begin_time', \swoole_table::TYPE_INT, 8 );//worker process begin time
        $this->swWorkingTaskProcesses->column( 'task_begin_time', \swoole_table::TYPE_INT, 8 );//specified task begin time
        $this->swWorkingTaskProcesses->create();
    }

    public function initTaskWorker( $server, $worker_id )
    {
        if( $server->taskworker )
        {
            $this->taskLock->lock();

            $iIndex = $this->swSpareTaskProcesses->count();

            echo 'taskID-t--: ', $iIndex, PHP_EOL;

            $this->swSpareTaskProcesses->set( $iIndex,
                [
                    'task_id' => $worker_id,
                    'task_index' => $iIndex,
                    'worker_pid' => -1,
                    'taskLabel' => '',
                    'worker_begin_time_readable' => date( 'Y-m-d H:i:s', time() ),
                    'task_begin_time_readable' => '',
                    'worker_begin_time' => time(),
                    'task_begin_time' => time(),
                ]);


            $this->taskLock->unlock();

            @swoole_set_process_name("task worker id: " . $worker_id . ' pid is: ' . $server->worker_pid );

        }
        else
        {
            @swoole_set_process_name("event worker id: " . $worker_id . ' pid is: ' . $server->worker_pid );
        }

    }


    public function getWorkerStatusInfo()
    {
        $arrRet = [];
        foreach( $this->swWorkingTaskProcesses as $process )
        {
            $arrRet[] = $process;
        }
        return $arrRet;
    }

    public function dispatchTask( $taskLabel, $processNum, $http )
    {
        $taskInfo = $this->getTaskWorker($taskLabel, $processNum );

        if ($taskInfo['code'] == self::TASK_OK)
        {
            foreach( $taskInfo[ 'tasks' ] as $task )
            {
                $http->task(
                    json_encode( [ 'taskInfo' => $task ] )
                );
            }

        }
        else
        {
            echo $taskInfo['msg'], PHP_EOL;
        }

        $arrResInfo[] = $taskInfo;

        return $arrResInfo;
    }

    public function checkRequestValid( $server )
    {
        if( $server['remote_addr'] == '127.0.0.1' )
        {
            return true;
        }

        return false;
    }



    public function getValidTasks( $arrTasks )
    {
        $arrTasksName = array_keys( $arrTasks );

        $arrResTasks = [];
        foreach( $arrTasksName as $taskLabel )
        {
            if( array_key_exists( strtolower( $taskLabel ), $this->_arrValidTasks ) &&
                $this->_arrValidTasks[ $taskLabel ]['enable'] )
            {

                if( $arrTasks[ $taskLabel ] == 0 )
                {
                    $arrResTasks[ $taskLabel ] = $this->_arrValidTasks[ $taskLabel ]['process_num'];
                }
                else
                {
                    $arrResTasks[ $taskLabel ] = $arrTasks[ $taskLabel ];
                }

            }
        }

        return $arrResTasks;
    }


    //private function getTaskProcessNum( $taskLabel )
    //{
    //    return $this->_arrValidTasks[ $taskLabel ]['process_num'];
    //}

    private function getTaskWorker( $taskLabel, $taskProcessCnt = 0 )
    {
        //$taskProcessCnt = $this->getTaskProcessNum( $taskLabel );

        //$taskProcessCnt = $processNum;

        $this->taskLock->lock();

        foreach( $this->swWorkingTaskProcesses as $key => $task )
        {
            if( $task[ 'taskLabel' ] == $taskLabel )
            {
                $this->taskLock->unlock();

                return [ 'code' => self::TASK_TAKEN, 'msg' => '已经启动无须再启动' ];
            }
        }

        if( $this->swSpareTaskProcesses->count() < $taskProcessCnt )
        {
            echo 'task worker进程已经分配完毕, 请检查后再试！', PHP_EOL;

            $this->taskLock->unlock();

            return [ 'code' => self::TASK_NOT_ENOUGH, 'msg' => 'task worker进程不足，请检查后再试！' ];
        }

        $arrSpareTasks = [];

        $iSpareCnt = 0;
        foreach( $this->swSpareTaskProcesses as $key => $task )
        {
            $task['taskLabel'] = $taskLabel;

            $this->swWorkingTaskProcesses->set( $key, $task );

            $this->swSpareTaskProcesses->del( $key );

            $arrSpareTasks[] = $task;

            ++$iSpareCnt;
            if( $iSpareCnt == $taskProcessCnt )
            {
                break;
            }

        }

        $this->taskLock->unlock();

        return array_merge( [ 'code' => self::TASK_OK, 'msg' => 'ok', 'tasks' => $arrSpareTasks ] );

    }

    public function resetTask( $taskInfo )
    {
        echo 'releasing: ' . $taskInfo->task_index, PHP_EOL;

        $this->taskLock->lock();

        foreach( $this->swWorkingTaskProcesses as $key => $task )
        {
            if( $task[ 'task_index' ] == $taskInfo->task_index )
            {

                $task['taskLabel'] = '';
                $task['worker_pid'] = -1;

                $this->swSpareTaskProcesses->set( $key, $task );

                $this->swWorkingTaskProcesses->del( $key );
            }
        }


        $this->taskLock->unlock();

    }








    public function changeWorkerStatus( $server, $taskInfo )
    {
        $this->taskLock->lock();

        $this->swWorkingTaskProcesses->set( $taskInfo->task_index, [ 'worker_pid' => $server->worker_pid ] );

        $this->taskLock->unlock();
    }

    public function doTaskWork( $server, $task_id, $reactor_id, $arrParams, $di )
    {
        $taskLabel = $arrParams->taskInfo->taskLabel;

        $taskCfg = $this->_arrValidTasks[ $taskLabel ];

        $clsName = $taskCfg['class'];

        $task = new $clsName();

        if( $task instanceof \Phalcon\Di\InjectionAwareInterface )
        {
            assert( $di );

            $task->setDI( $di );
        }

        $task->run($server, $task_id, $reactor_id, $arrParams);

    }


    /**
     * @return null
     */
    public function getArrValidTasks()
    {
        return $this->_arrValidTasks;
    }

    /**
     * @param null $arrValidTasks
     */
    public function setArrValidTasks($arrValidTasks): void
    {
        $this->_arrValidTasks = $arrValidTasks;
    }
}